package com.ddtj.demo1.component.aspect;

import com.alibaba.fastjson.JSON;
import com.ddtj.demo1.component.annotation.Log;
import com.ddtj.demo1.component.utils.IpUtils;
import com.ddtj.demo1.component.utils.ServletUtils;
import com.ddtj.demo1.component.utils.StringUtils;
import com.ddtj.demo1.pojo.HttpStatus;
import com.ddtj.demo1.dao.OperLogJpa;
import com.ddtj.demo1.entity.OperLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;

@Component
@Aspect
public class LogAspect {
    @Autowired
    OperLogJpa operLogJpa;
    @Pointcut("@annotation(com.ddtj.demo1.component.annotation.Log)")
    public void logPointCut(){}

    @AfterReturning(pointcut = "logPointCut()",returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint,Object jsonResult){
        handleLog(joinPoint,null,jsonResult);
    }
    @AfterThrowing(pointcut = "logPointCut()",throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint,Exception e){
        handleLog(joinPoint,e,null);
    }

    public void handleLog(final JoinPoint joinPoint,final Exception e,Object jsonResult){
        Log log = getAnnotationLog(joinPoint);
        if(null == log){
            return;
        }
        OperLog operLog = new OperLog();
        operLog.setOperTime(new Date());
        operLog.setRequestMthod(ServletUtils.getRequest().getMethod());
        operLog.setOperIP(IpUtils.getIpAddr(ServletUtils.getRequest()));
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        operLog.setMethodName(className+"."+methodName+"()");
        getControllerMethodDescription(joinPoint,log,operLog);
        int status = HttpStatus.SUCCESS;
        if(null != e){
            String message = e.getMessage();
            message = StringUtils.substring(message,0,2550);
            operLog.setErrMsg(message);
            status = HttpStatus.ERROR;
        }
        operLog.setStatus(status);
        String jsonString = JSON.toJSONString(jsonResult);
        if(jsonString.length()>2550){
            jsonString = jsonString.substring(0,2550);
        }
        operLog.setJsonResult(jsonString);
        operLogJpa.save(operLog);

    }
    private Log getAnnotationLog(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if(null != method){
            Log log = method.getAnnotation(Log.class);
            return log;
        }
        return null;
    }
    private void getControllerMethodDescription(JoinPoint joinPoint,Log log,OperLog operLog){
        operLog.setTitle(log.title());
        if(log.isSaveRequestData()){
            setRequestValue(joinPoint,operLog);
        }
        System.out.printf(operLog.getTitle());
    }

    private void setRequestValue(JoinPoint joinPoint, OperLog operLog) {
        String requestMthod = operLog.getRequestMthod();
        if (HttpMethod.PUT.name().equals(requestMthod) || HttpMethod.POST.name().equals(requestMthod)){
            Object[] args = joinPoint.getArgs();

            String params = argsArrayToString(args);
            if(params.length()>2550){
                params = params.substring(0,2550);
            }
            operLog.setOperParm(params);
        }
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (int i = 0; i < paramsArray.length; i++)
            {
                if (!isFilterObject(paramsArray[i]))
                {
                    Object jsonObj = JSON.toJSON(paramsArray[i]);
                    params += jsonObj.toString() + " ";
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o){
        Class<?> clazz = o.getClass();
        if (clazz.isArray())
        {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        }
        else if (Collection.class.isAssignableFrom(clazz))
        {
            Collection collection = (Collection) o;
            for (Iterator iter = collection.iterator(); iter.hasNext();)
            {
                return iter.next() instanceof MultipartFile;
            }
        }
        else if (Map.class.isAssignableFrom(clazz))
        {
            Map map = (Map) o;
            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();)
            {
                Map.Entry entry = (Map.Entry) iter.next();
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}
